package com.lolaage.tracks.adapter;

import java.util.Vector;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Handler;
import android.util.Log;

import com.baidu.mapapi.BMapManager;
import com.baidu.mapapi.LocationListener;
import com.baidu.mapapi.MKLocationManager;
import com.lolaage.tracks.MyLocationPolicy;
import com.lolaage.tracks.MyLocationPrivider;
import com.lolaage.tracks.MyLocationValider;
import com.lolaage.tracks.util.CycledThread;
import com.lolaage.tracks.util.CycledThread.OnTimeoutListener;

/*********************************************************
 * @author : zhaohua
 * @version : 2012-9-7
 * @see : 百度地图管理器
 * @Copyright : copyrights reserved by personal 2007-2011
 **********************************************************/
public class MyBMapManager implements MyLocationPrivider
{
    final static String TAG = "MyBMapManager";
    // 5087F4E6BE0642A53091725FE769CD4B62FA9F1E 1FE91619DB21A03EF42A5892D69CEB33C1ED1648
    public static final String mKey = "5087F4E6BE0642A53091725FE769CD4B62FA9F1E";
       
    private BMapManager mBMapMan = null;
    protected Context mContext;
    private CycledThread mLocKeepThread;
    private CycledThread mLocTask;
    private MyLocationPolicy mLocationListenerPolicy;
    private MyLocationValider mLocationValider;
    private long currentRecordingInterval;
    boolean isLocating = false;
    private int     mLocRetryCount;
    private Handler mHandler = new Handler();
    private Vector<OnLocationListener> mLocListeners = new Vector<OnLocationListener>();
    /** 当前最好一个有效的位置 */
    private Location currentBestLocation;
    /** 定位模式，GPS优先还是Network优先*/
    private int mLocMode = L_NETWORK_ONLY;
    
    private static MyBMapManager instance;
    public static MyBMapManager getInstace(Context context)
    {
        if (instance == null)
        {
            instance = new MyBMapManager(context);
        }
        return instance;
    }
    private MyBMapManager(Context context)
    {
        mContext = context;
        mBMapMan = new BMapManager(context);
        mBMapMan.init(mKey, null);
        setPrivity(mLocMode);
        mBMapMan.getLocationManager().setLocationCoordinateType(MKLocationManager.MK_COORDINATE_WGS84);
        
        mLocationValider = new LastLocationValider();
        mLocationListenerPolicy = new AdaptiveLocationPolicy(10000, 100000, 10, 50);        
    }
    
    /**
     * 在引用的activity的onResume中执行
     */
    public void start()
    {
        mBMapMan.start();
    }

    /**
     * 在引用的activity的onPause中执行
     */
    public void stop()
    {
        mBMapMan.stop();
    }

    /**
     * 当不需要地图的时候引用，通常是onTerminate
     */
    public void destory()
    {
        if (mBMapMan != null)
        {
            unregisterLocationListener();
            mBMapMan.destroy();
            mBMapMan = null;
        }
    }

    public BMapManager getBMapManager()
    {
        return mBMapMan;
    }
   
    public void setPrivity(int locMode)
    {
        if(locMode < L_AUTO || locMode > L_NETWORK_ONLY)
        {
            mLocMode = L_AUTO;
        }else
        {
            mLocMode = locMode;
        }
        
        tempChangePrivity(mLocMode);
    }
    
    private void tempChangePrivity(int locMode)
    {
        if(locMode == L_GPS_FIRST || locMode == L_GPS_ONLY)
        {
            mBMapMan.getLocationManager().enableProvider(MKLocationManager.MK_GPS_PROVIDER);
            mBMapMan.getLocationManager().disableProvider(MKLocationManager.MK_NETWORK_PROVIDER);
        }else
        if(locMode == L_NETWORK_ONLY)
        {
            mBMapMan.getLocationManager().disableProvider(MKLocationManager.MK_GPS_PROVIDER);
            mBMapMan.getLocationManager().enableProvider(MKLocationManager.MK_NETWORK_PROVIDER);
        }else
        {
            mBMapMan.getLocationManager().enableProvider(MKLocationManager.MK_GPS_PROVIDER);
            mBMapMan.getLocationManager().enableProvider(MKLocationManager.MK_NETWORK_PROVIDER);
        }
    }
    
    public void addLocationListener(OnLocationListener listener)
    {
        if(listener != null && !mLocListeners.contains(listener))
        {
            mLocListeners.add(listener);
        }
    }
    
    public void rmvLocationListener(OnLocationListener listener)
    {
        if(listener != null && mLocListeners.contains(listener))
        {
            mLocListeners.remove(listener);
        }
    }
    
    public void startKeepLocation(int intalTime)
    {
        if(mLocKeepThread == null)
        {
            mLocKeepThread = new CycledThread(new Runnable()
            {
                @Override
                public void run()
                {
                    Log.d(TAG, "-->startKeepLocation start ...");
                    mHandler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            startLocation(true,0);
                        }
                    }); 
                }
            }, intalTime);
        }
        if(!mLocKeepThread.isAlive())
        {
            mLocKeepThread.start();
        }        
    }
    
    public void stopKeepLocation()
    {
        if(mLocKeepThread != null)
        {
            mLocKeepThread._stop();
            mLocKeepThread = null;
        }
        unregisterLocationListener();
    }
      
    public void setLocationOption(int minTime, int minDist)
    {
        mLocationListenerPolicy.updateIdleTime(minTime,minDist);
        if(currentRecordingInterval != mLocationListenerPolicy.getDesiredPollingInterval())
        {
            int interval = (int)(mLocationListenerPolicy.getDesiredPollingInterval() / 1000);
            Log.d(TAG, "--->interval = " + interval);
            mBMapMan.getLocationManager().setNotifyInternal(interval, interval);
        }
    }
    
    public void setLocationPolicy(MyLocationPolicy policy)
    {
        if(policy == null) return;
        mLocationListenerPolicy = policy;
    }
    
    public void setLocationValider(MyLocationValider valider)
    {
        if(valider == null) return;
        mLocationValider = valider;
    }
    
    /**
     * 开始进行定位
     * @param update : 是否立即更新位置
     * @param timeout : 超时处理的时间
     * */
    public void startLocation(boolean update, final int timeout)
    {
        // 定位检核
        if(currentBestLocation != null && !update)
        {
            Location lastLoc = mBMapMan.getLocationManager().getLocationInfo();
            if(mLocationValider.isBetterLocation(currentBestLocation,lastLoc))
            {
                currentBestLocation = lastLoc;
                mHandler.postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        // 获取缓存最新
                        onLocOK();
                    }
                },3000);
                return;
            }
        }
        if(isLocating())
        {
            // 正在定位
            return;
        }
        try{
            registerLocationListener();
            if(timeout > 5000 || mLocMode == L_GPS_FIRST)
            {
            mLocTask = new CycledThread(200, timeout > 5000 ? timeout : 5000, new OnTimeoutListener()
            {
                @Override
                public void onTimeout() 
                {
                    mHandler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            mLocTask = null;
                            unregisterLocationListener();
                            if(mLocRetryCount >=3 )
                            {
                                // 定位三次失败，则认定为定位失败
                                if(mLocMode == L_GPS_FIRST)
                                {                                   
                                    if(mLocRetryCount >= 3 && mLocRetryCount < 6)
                                    {
                                        Log.d(TAG, "---> GPS loc error, switch network loc!");
                                        tempChangePrivity(L_AUTO);
                                        startLocation(true,timeout);
                                        mLocRetryCount++;
                                    }else
                                    if(mLocRetryCount >= 6)
                                    {
                                        Log.d(TAG, "---> GPS loc error, network loc fail!");
                                        tempChangePrivity(L_GPS_FIRST);
                                        onLocErr();
                                    }
                                }else
                                {
                                    onLocErr();
                                }
                            }else 
                            {
                                Log.d(TAG, "-->re location count = " + mLocRetryCount);
                                mLocRetryCount++;
                                startLocation(true,timeout);
                            }
                        }
                    });
                }
            });
            mLocTask.start();
            }
        } catch (Exception e) 
        {
            Log.w(TAG, e);
            //重复启动容错
        }
    }
    
    public boolean isLocating()
    {
        return mLocTask != null && mLocTask.isAlive();
    }
    
    private void registerLocationListener()
    {
        unregisterLocationListener();
        if (mBMapMan == null)
        {
            Log.e(TAG, "locationManager is null.");
            return;
        }
        try
        {
            int interval = (int)(mLocationListenerPolicy.getDesiredPollingInterval() / 1000);
            mBMapMan.getLocationManager().setNotifyInternal(interval, interval);
            mBMapMan.getLocationManager().requestLocationUpdates(mLocListener);
            currentRecordingInterval = interval;
            mBMapMan.start();
            isLocating = true;
            Log.d(TAG, "---> registerLocationListener interval = " + interval);
        } catch (RuntimeException e)
        {
            Log.e(TAG, "Could not register location listener.", e);
        }
    }

    private void unregisterLocationListener()
    {
        if (mBMapMan == null)
        {
            Log.e(TAG, "locationManager is null.");
            return;
        }
        mBMapMan.getLocationManager().removeUpdates(mLocListener);
        isLocating= false;
        Log.d(TAG, "---> unregisterLocationListener ");
    }
    
    private void onLocOK()
    {
        mLocRetryCount = 0;
        Log.d(TAG, "-->get location:" + currentBestLocation.getLongitude() + "," + currentBestLocation.getLatitude());
        for(OnLocationListener listener : mLocListeners)
        {
            listener.onLocationChanged(currentBestLocation);
        }
    }

    private void onLocErr()
    {
        mLocRetryCount = 0;
        Log.d(TAG, "-->get location timeout ");
        Location lastLoc = mBMapMan.getLocationManager().getLocationInfo();
        if (mLocationValider.isBetterLocation(currentBestLocation,lastLoc))
        {
            currentBestLocation = lastLoc;
            onLocOK();
        } else
        {
            for (OnLocationListener listener : mLocListeners)
            {
                listener.onLocationError();
            }
        }
    }
    
    private LocationListener mLocListener = new LocationListener()
    {
        @Override
        public void onLocationChanged(Location loc)
        {
            Log.d(TAG, "--> onLocationChanged reached!" + loc);
            if(loc.getProvider().equals(LocationManager.NETWORK_PROVIDER))
            {
                loc.setTime(System.currentTimeMillis());
                if(mLocMode == L_GPS_FIRST)
                {
                    tempChangePrivity(L_GPS_FIRST);
                }
            }
            if (mLocationValider.isBetterLocation(currentBestLocation,loc))
            {
                if (mLocTask != null)
                {
                    mLocTask._stop();
                    mLocTask = null;
                }                
                currentBestLocation = loc;                
                onLocOK();
            }
        }
    };
}
